#include <algorithm>
#include "GUIWindow.h"
#include "IRenderer.h"
#include "Matrix.h"
#include "OnKeyPressEvent.h"
#include "OnMouseLeftDownEvent.h"
#include "OnMouseLeftUpEvent.h"
#include "OnMouseMoveEvent.h"
#include "OnWindowResizeEvent.h"

std::vector<GUIWindow*> GUIWindow::Windows;

GUIWindow::GUIWindow()
{
	m_docking = 0;
	m_fontColor = 0x00000000;
	m_font = HashedString("Arial");
	m_visible = true;
	m_enabled = true;
	Windows.push_back(this);
}
GUIWindow::~GUIWindow()
{
	std::remove(Windows.begin(), Windows.end(), this);
}
void GUIWindow::Paint()
{
	if (m_visible)
	{
		// Render window
		Maths::Matrix transform;
		transform.Translate((float)m_location.X, (float)m_location.Y, 0.0f);
		transform.Scale((float)m_dimensions.X, (float)m_dimensions.Y, 1.0f);
		
		Renderer->SetColor(0xFFDDDDDD);
		Renderer->PushTexture(m_background);
		Renderer->PushModelViewMatrix(transform);
		Renderer->Render(*(m_shape.getShape()));
		Renderer->PopModelViewMatrix();
		Renderer->PopTexture();

		// Render Title
		transform.Identity();
		transform.Translate((float)m_location.X+2, (float)m_location.Y+2, 0.0f);
		transform.Scale((float)m_dimensions.X-4, 16, 1.0f);

		Renderer->SetColor(0, 128, 255, 255);
		Renderer->PushTexture(m_background);
		Renderer->PushModelViewMatrix(transform);
		Renderer->Render(*(m_shape.getShape()));
		Renderer->PrintFont(m_font, m_fontColor, m_parentLocation.X + m_location.X + 4, m_parentLocation.Y + m_location.Y + 4, m_title.c_str());
		Renderer->PopModelViewMatrix();
		Renderer->PopTexture();
		Renderer->SetColor(0xFFFFFFFF);
		
		transform.Identity();
		transform.Translate((float)m_location.X, (float)m_location.Y + 20, 0.0f);
		Renderer->PushModelViewMatrix(transform);

		for (IGUIElement::Vector_t::const_iterator it = m_children.begin(); it != m_children.end(); ++it)
			(*it)->Paint();

		Renderer->PopModelViewMatrix();
	}
}
void GUIWindow::ParentLocation( Maths::Point<unsigned int>& location )
{
	IGUIElement::ParentLocation(location);
	for (IGUIElement::Vector_t::iterator it = m_children.begin(); it != m_children.end(); ++it)
		(*it)->ParentLocation(Maths::Point<unsigned int>(m_location.X + m_parentLocation.X, m_location.Y  + 20 + m_parentLocation.Y));
}
void GUIWindow::ClearChildren()
{
	m_children.clear();
}
void GUIWindow::AddChild( Pointer<IGUIElement> element )
{
	m_children.push_back(element);
	element->ParentLocation(Maths::Point<unsigned int>(m_location.X + m_parentLocation.X, m_location.Y  + 20 + m_parentLocation.Y));
	element->Parent(this);
}
void GUIWindow::Location( Maths::Point<unsigned int>& location )
{
	m_location = location;

	for (IGUIElement::Vector_t::iterator it = m_children.begin(); it != m_children.end(); ++it)
		(*it)->ParentLocation(Maths::Point<unsigned int>(m_location.X + m_parentLocation.X, m_location.Y  + 20 + m_parentLocation.Y));
}
void GUIWindow::Title( std::string& title )
{
	m_title = title;
}
std::string GUIWindow::Title() const
{
	return m_title;
}
void GUIWindow::Font( std::string& font )
{
	m_font = HashedString(font);
}
void GUIWindow::Font( HashedString& font )
{
	m_font = font;
}
HashedString GUIWindow::Font() const
{
	return m_font;
}
void GUIWindow::FontColor( unsigned int color )
{
	m_fontColor = color;
}
unsigned int GUIWindow::FontColor() const
{
	return m_fontColor;
}
void GUIWindow::Background( std::string& background )
{
	m_background = HashedString(background);
}
void GUIWindow::Background( HashedString& background )
{
	m_background = background;
}
HashedString GUIWindow::Background() const
{
	return m_background;
}
bool GUIWindow::ReceiveEvent( IEvent& event )
{
	if (m_enabled && m_visible)
	{
		if (event.Type() == OnMouseLeftDownEvent::type)
		{
			OnMouseLeftDownEvent& mouseEvent = static_cast<OnMouseLeftDownEvent&>(event);
			int x = mouseEvent.Coords().X;
			int y = mouseEvent.Coords().Y;
			int absX = m_location.X + m_parentLocation.X;
			int absY = m_location.Y + m_parentLocation.Y;
		
			if ((x > absX) && (x < absX + (int)m_dimensions.X) &&
				(y > absY) && (y < absY + (int)m_dimensions.Y))
			{
				m_hasFocus = true;

				for (IGUIElement::Vector_t::iterator it = m_children.begin(); it != m_children.end(); ++it)
				{
					if ((*it)->ReceiveEvent(event))
						return true;
				}

				for (std::vector<GUIWindow*>::iterator it = Windows.begin(); it != Windows.end(); ++it)
				{
					(*it)->ActiveObject(0);
					(*it)->m_hasFocus = false;
				}
				return true;
			} else
			{
				m_hasFocus = false;
				m_activeElement = 0;
			}
		}
		else if (event.Type() == OnMouseLeftUpEvent::type)
		{
			OnMouseLeftUpEvent& mouseEvent = static_cast<OnMouseLeftUpEvent&>(event);
			int x = mouseEvent.Coords().X;
			int y = mouseEvent.Coords().Y;
			int absX = m_location.X + m_parentLocation.X;
			int absY = m_location.Y + m_parentLocation.Y;
		
			if ((x > absX) && (x < absX + (int)m_dimensions.X) &&
				(y > absY) && (y < absY + (int)m_dimensions.Y))
			{
				m_hasFocus = true;

				for (IGUIElement::Vector_t::iterator it = m_children.begin(); it != m_children.end(); ++it)
				{
					if ((*it)->ReceiveEvent(event))
						return true;
				}
				return true;
			}
		}
		else if (event.Type() == OnKeyPressEvent::type)
		{
			OnKeyPressEvent& keyEvent = static_cast<OnKeyPressEvent&>(event);

			if (!keyEvent.isVirtual())
			{
				switch (keyEvent.Key())
				{
				case 13:				// Enter key
					{
						if (m_hasFocus)
						{
							if (m_enterFunction.isValid())
								(*m_enterFunction)();
							return true;
						} else
						{
							return false;
						}
					}
				}
			}
			if (m_activeElement)
				return m_activeElement->ReceiveEvent(event);
		} else if (event.Type() == OnWindowResizeEvent::type)
		{
			OnResizeEvent(event);
			for (IGUIElement::Vector_t::iterator it = m_children.begin(); it != m_children.end(); ++it)
			{
				if ((*it)->ReceiveEvent(event))
					return true;
			}
		} else if (event.Type() == OnMouseMoveEvent::type)
		{
			for (IGUIElement::Vector_t::iterator it = m_children.begin(); it != m_children.end(); ++it)
			{
				if ((*it)->ReceiveEvent(event))
					return true;
			}
		}
	}
	return false;
}
void GUIWindow::EnterFunction( Pointer<Functor>& function )
{
	m_enterFunction = function;
}

void GUIWindow::Dock( unsigned int docking )
{
	m_docking = docking;
}

bool GUIWindow::OnResizeEvent( IEvent& event )
{
	OnWindowResizeEvent& resize = static_cast<OnWindowResizeEvent&>(event);

	// Width
	if ((m_docking & LEFT) && (m_docking & RIGHT))
	{
		m_location.X = 0;
		m_dimensions.X = resize.Size().X;
	} else if (m_docking & LEFT)
	{
		m_location.X = 0;
	} else if (m_docking & RIGHT)
	{
		m_location.X = resize.Size().X - m_dimensions.X;
	}
	// Height
	
	if ((m_docking & TOP) && (m_docking & BOTTOM))
	{
		m_location.Y = 0;
		m_dimensions.Y = resize.Size().Y;
	} else if (m_docking & TOP)
	{
		m_location.Y = 0;
	} else if (m_docking & BOTTOM)
	{
		m_location.Y = resize.Size().Y - m_dimensions.Y;
	}
	
	for (IGUIElement::Vector_t::iterator it = m_children.begin(); it != m_children.end(); ++it)
		(*it)->ParentLocation(Maths::Point<unsigned int>(m_location.X + m_parentLocation.X, m_location.Y  + 20 + m_parentLocation.Y));
	return false;
}

Maths::Point<unsigned int> GUIWindow::Location() const
{
	return IGUIElement::Location();
}